package com.canyou.basic.service.impl;

import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.canyou.basic.entity.sms.SmsSendVO;
import com.canyou.basic.entity.sms.SmsStatusEnum;
import com.canyou.basic.mapper.SmsSendMapper;
import com.canyou.basic.model.SmsSend;
import com.canyou.basic.service.SmsSendService;
import com.canyou.enums.ErrorCodeEnum;
import com.canyou.exception.CysBusinessException;
import com.canyou.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author canyou
 * @since 2020-06-28
 */
@Service
@Slf4j
public class SmsSendServiceImpl extends ServiceImpl<SmsSendMapper, SmsSend> implements SmsSendService {
    private static final Marker DING_MARKER = MarkerFactory.getMarker("ding-ding");

    /**
     * 登录验证码模板code
     */
    private static final String LOGIN_CODE_TEMPLATE_CODE = "SMS_189560896";

    @Autowired
    private DySmsUtils dySmsUtils;

    @Override
    public boolean sendSms(Integer smsType, String smsTemplateCode, String phoneNumber, Map<String, String> para) {
        LocalDateTime now = LocalDateTime.now();
        final SmsSend smsSend = new SmsSend()
                .setSmsType(smsType)
                .setTemplateCode(smsTemplateCode)
                .setPhoneNumber(phoneNumber)
                .setPara(JsonUtils.toString(para))
                .setIsSend(0)
                .setIsResend(0)
                .setSendStatus(1);
        try {
            SendSmsResponse sendSmsResponse = dySmsUtils.send(smsTemplateCode, phoneNumber, para);
            smsSend.setBizId(sendSmsResponse.getBizId())
                    .setIsSend(1)
                    .setSendDate(now);
            boolean isSuccess = dySmsUtils.checkSendSuccess(sendSmsResponse);
            if (isSuccess) {
                smsSend.setErrCode(sendSmsResponse.getCode())
                        .setErrMsg(sendSmsResponse.getMessage())
                        .setSendStatus(3);
                return true;
            } else {
                throw new ClientException(sendSmsResponse.getCode(), sendSmsResponse.getMessage(), sendSmsResponse.getRequestId());
            }
        } catch (ClientException ex) {
            smsSend.setErrCode(ex.getErrCode())
                    .setErrMsg(ex.getErrMsg())
                    .setSendStatus(2);
            throw new CysBusinessException(ErrorCodeEnum.SEND_SMS_ERROR, ex.getErrMsg());
        } catch (Exception ex) {
            smsSend.setErrMsg(ex.getMessage())
                    .setSendStatus(2);
            throw new CysBusinessException(ErrorCodeEnum.SEND_SMS_ERROR, ex.getMessage());
        } finally {
            // 1. 记录短信发送到数据库
            boolean res = baseMapper.insert(smsSend) > 0;
            if (!res) {
                log.warn(DING_MARKER, "保存短信发送记录【" + smsSend + "】失败");
            }
            // 短信确实发送失败时不需要回查发送结果
            String bizId = smsSend.getBizId();
            if (StringUtils.isNotBlank(bizId)) {
                DelayUtils.delay(() -> syncSmsSendReceipt(smsSend), 60);
            }
        }
    }

    /**
     * 同步短信发送回执
     *
     * @param smsSend
     * @return
     */
    @Override
    public Boolean syncSmsSendReceipt(final SmsSend smsSend) {
        // 2. 如果有bizId, 则回查发送结果
        String bizId = smsSend.getBizId();
        String phoneNumber = smsSend.getPhoneNumber();
        LocalDateTime sendDate = smsSend.getSendDate();
        if (StringUtils.isNotBlank(bizId)) {
            try {
                QuerySendDetailsResponse.SmsSendDetailDTO smsSendDetailDTO = dySmsUtils.querySendDetail(phoneNumber, bizId, sendDate);
                if (smsSendDetailDTO != null) {
                    smsSend.setSendStatus(smsSendDetailDTO.getSendStatus().intValue())
                            .setErrCode(smsSendDetailDTO.getErrCode())
                            .setSendDate(DateUtils.parse2LocalDateTime(smsSendDetailDTO.getSendDate()))
                            .setReceiveDate(DateUtils.parse2LocalDateTime(smsSendDetailDTO.getReceiveDate()))
                            .setContent(smsSendDetailDTO.getContent());
                    // 3. 更新短信发送回查结果到数据库
                    baseMapper.updateById(smsSend);
                    // status为3表示短信已经送达, 为2表示发送失败, 为1表示等待回执, 只有在为1时才需要再次请求获取回执
                    if (SmsStatusEnum.SUCCESS.getValue().equals(smsSendDetailDTO.getSendStatus().intValue())) {
                        return true;
                    }
                }
            } catch (Exception ex) {
                log.warn("查询短信发送状态异常: " + ex.getMessage(), ex);
            }
        }
        return null;
    }

    @Override
    public IPage<SmsSendVO> getPageList(IPage<SmsSendVO> page, String condition) {
        return baseMapper.getPageList(page, condition);
    }

    @Override
    public boolean reSendSms(SmsSend smsSendRecord) {
        try {
            Map<String, String> para = JsonUtils.toObject(smsSendRecord.getPara(), Map.class);
            return sendSms(smsSendRecord.getSmsType(), smsSendRecord.getTemplateCode(), smsSendRecord.getPhoneNumber(), para);
        } catch (Exception ex) {
            log.warn(DING_MARKER, "【" + smsSendRecord + "】短信发送记录, 重发失败: " + ex.getMessage(), ex);
        } finally {
            smsSendRecord.setIsSend(1).setIsResend(1);
            boolean res = updateById(smsSendRecord);
            if (!res) {
                log.warn(DING_MARKER, "更新【" + smsSendRecord + "】短信发送记录为已重发时失败");
            }
        }
        return false;
    }

    @Override
    public boolean sendLoginCode(String phoneNumber, String code) {
        Map<String, String> para = new HashMap<>(1);
        para.put("code", code);
        boolean isSuccess = sendSms(1, LOGIN_CODE_TEMPLATE_CODE, phoneNumber, para);
        if (isSuccess) {
            // TODO: 短信验证码发送成功需要做一些事情
        }
        return isSuccess;
    }

}
